package nachos.threads;

import nachos.machine.*;

////////////////////  BEGIN HENRI /////////////////////////
import java.util.LinkedList;
import java.util.Iterator;
//////////////////// END HENRI /////////////////////////


/**
 * An implementation of condition variables that disables interrupt()s for
 * synchronization.
 *
 * <p>
 * You must implement this.
 *
 * @see	nachos.threads.Condition
 */
public class Condition2 {
    /**
     * Allocate a new condition variable.
     *
     * @param	conditionLock	the lock associated with this condition
     *				variable. The current thread must hold this
     *				lock whenever it uses <tt>sleep()</tt>,
     *				<tt>wake()</tt>, or <tt>wakeAll()</tt>.
     */
    public Condition2(Lock conditionLock) {
	this.conditionLock = conditionLock;
        ////////////////////  BEGIN HENRI /////////////////////////
	/* Create the queue of waiting threads */
        this.conditionQueue = new LinkedList<KThread>();
        //////////////////// END HENRI /////////////////////////
    }

    /**
     * Atomically release the associated lock and go to sleep on this condition
     * variable until another thread wakes it using <tt>wake()</tt>. The
     * current thread must hold the associated lock. The thread will
     * automatically reacquire the lock before <tt>sleep()</tt> returns.
     */
    public void sleep() {

        /* If the current thread doesn't hold the lock, then abort */
	Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        /////////////////  BEGIN HENRI ///////////////////////
	/* Disable interrupts */
        boolean intStatus = Machine.interrupt().disable();

	/* Add the current thread to the end of the queue */
        conditionQueue.add(KThread.currentThread());


        /* Release the lock */
	conditionLock.release();

	/* Put the current thread in the blocked state */
	KThread.currentThread().sleep();

     	/* Restore interrupt status */
	Machine.interrupt().restore(intStatus);

	/* Reacquire the lock */
	conditionLock.acquire();

        ///////////////// END HENRI ///////////////////////
    }

    /**
     * Wake up at most one thread sleeping on this condition variable. The
     * current thread must hold the associated lock.
     */
    public void wake() {
	Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        /////////////////  BEGIN HENRI ///////////////////////

 	/* Disable interrupts */	
        boolean intStatus = Machine.interrupt().disable();

	/* Remove the first thread from the queue, if any, and put it
         * in the unblocked state */
	if (conditionQueue.size() > 0) {
	  KThread toUnblock = (KThread) conditionQueue.removeFirst();
          toUnblock.ready();
        }

     	/* Restore interrupt status */
	Machine.interrupt().restore(intStatus);

        ///////////////// END HENRI ///////////////////////

    }

    /**
     * Wake up all threads sleeping on this condition variable. The current
     * thread must hold the associated lock.
     */
    public void wakeAll() {
	Lib.assertTrue(conditionLock.isHeldByCurrentThread());

        /////////////////  BEGIN HENRI ///////////////////////
 	/* Disable interrupts */	
        boolean intStatus = Machine.interrupt().disable();

	/* Remove each thread from the queue and put it inthe ready state */
	while (conditionQueue.size() > 0) {
          KThread toUnblock = (KThread) conditionQueue.removeFirst();
          toUnblock.ready();
        }

     	/* Restore interrupt status */
	Machine.interrupt().restore(intStatus);

        ///////////////// END HENRI ///////////////////////

    }

    /**
     * Tests whether this module is working.
     */
    public static void selfTest() {
        Condition2Test.runTest();
    }

    private static final char dbgCondition = 'c';

    private Lock conditionLock;
    ////////////////////  BEGIN HENRI /////////////////////////
    private LinkedList<KThread> conditionQueue;
    //////////////////// END HENRI /////////////////////////
}
